# coding: utf-8

from __future__ import absolute_import, division, print_function, unicode_literals

import os.path
import re
import sys
from pathlib import Path

from six import PY2, u
from thriftpy2 import load
from thriftpy2.parser.lexer import keywords, thrift_reserved_keywords


def upper_camel_case(case):
    case = re.sub(r"[\s:{}]+", "", case)
    new_case = re.sub(r"(/:\w|[/_\-]\w)", lambda m: m.group(1)[1:].upper(), case)
    return new_case


def replace_thrift_keywords(fragment):
    pattern = r"(?:bool|i8|i16|i32|i64|byte|double|string|binary|list<[^>]*>|set<[^>]*>|map<[^>]*,\s*.*>|struct|exception|service)\s+\b({})\b".format(
        "|".join(keywords + thrift_reserved_keywords))
    fragment = re.sub(pattern, lambda m: m.group(0) + "_apitest_add", fragment)
    return fragment


def remove_keywords_suffix(text: str):
    text = text.replace("_apitest_add", "")
    return text


class ParseThrift:

    def __init__(self, psm, idl):
        if not os.path.exists(idl):
            raise FileExistsError("文件不存在，idl file is %s" % idl)
        self.psm = psm
        self.idl = idl
        # self.out_dir = out_dir
        self.new_idl = self.add_suffix(self.idl)
        self.parse_struct()
        if PY2:
            self.thrift = load(self.new_idl.encode("utf-8"))
        else:
            self.thrift = load(self.new_idl)
        if not hasattr(self.thrift, "__thrift_meta__"):
            sys.exit(0)
        self.meta = self.thrift.__thrift_meta__
        self.service_mate = {}

    @staticmethod
    def add_suffix(path):
        suffix = "_new"
        path = path.replace(os.path.basename(path), os.path.basename(path).replace(".", "{}.".format(suffix)))
        return path

    @staticmethod
    def rm_suffix(path):
        suffix = "_new"
        path = path.replace(os.path.basename(path), os.path.basename(path).replace("{}.".format(suffix), "."))
        return path

    def parse_struct(self):
        """主要处理返回字段名称和IDL定义的字段不一致，将处理成一致
        """
        lines = []
        with open(self.idl, "r") as f:
            content = f.read()
            for line in content.split("\n"):
                if "api.body" in line or "api.query" in line:
                    param = re.search(r"(?<=[\"'])\b\w+", line).group()
                    line = re.sub(r"\b\w+\b(?=\s*\()", param, line)
                if re.search(r"^\s+\d+:", line):
                    line = replace_thrift_keywords(line)
                lines.append(line)
            f.close()
        with open(self.new_idl, "w") as f:
            f.write("\n".join(lines))
            f.close()

    def parse_service(self):
        for service in self.meta["services"]:
            service_name = u(service.__name__)
            self.service_mate[service_name] = []
            for method_name in service.thrift_services:
                method_dict = {"method_name": method_name}
                args = getattr(service, "%s_args" % method_name)
                for _, spec in args.thrift_spec.items():
                    method_dict.update({"req": u(spec[2].__name__)})
                result = getattr(service, "%s_result" % method_name)
                for _, spec in result.thrift_spec.items():
                    method_dict.update({"resp": u(spec[2].__name__)})
                self.service_mate[service_name].append(method_dict)

    def parse_route(self):
        self.parse_service()
        with open(self.idl) as f:
            thrift = f.read()
        for service_name, values in self.service_mate.items():
            pattern = re.compile(rf"\bservice\s+{re.escape(service_name)}\s+\{{[\s\S]*?[\n\s]*}}")
            service = pattern.search(thrift).group()
            for line in service.split("\n"):
                if "api." not in line or re.search(r"^/{2,}", line.lstrip()):
                    continue
                method = re.search(r"(?<=api\.)\w+(?=\s*=)", line).group()
                route = re.search(r"(?<=[\"']).*?(?=[\"'])", line).group()
                route_name = upper_camel_case(route)
                for index, value in enumerate(values):
                    t_pattern = re.compile(r"\b" + value.get("method_name") + r"\b")
                    if t_pattern.search(line):
                        self.service_mate[service_name][index]["method"] = method
                        self.service_mate[service_name][index]["route"] = route
                        self.service_mate[service_name][index]["route_name"] = route_name

    def parse_idl_includes(self, idl, out_dir):
        with open(idl, "r") as f:
            thrift = f.read()
            for line in thrift.split("\n"):
                if line.strip().startswith("include"):
                    pt = re.search(r"(?<=[\"'])[./\\\w]*(?=[\"'])", line).group()
                    self.parse_idl_includes(os.path.normpath(os.path.join(Path(idl).parent, pt)), out_dir)
                    thrift = thrift.replace(line, "include \"./{}\"".format(Path(pt).name))

        with open(os.path.join(out_dir, os.path.basename(self.rm_suffix(idl))), "w") as o:
            f.close()
            o.write(thrift)
            o.close()
